﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.IO;

namespace Moe_v0._1
{
    class EnPrefixTreeModel
    {
        // Singleton pattern:
        private static EnPrefixTreeModel _instance = null;
        protected EnPrefixTreeModel() 
        {
            if (_modelFileFullName.Length > 0)
            {
                InitializeModelFromFile(_modelFileFullName);
            }
            else
            {
                InitializeModel();
            }
        }
        public static EnPrefixTreeModel Instance()
        {
            if (_instance == null)
            {
                _instance = new EnPrefixTreeModel();
            }
            Debug.Assert(_instance != null);
            return _instance;
        }

        private MoeNode _root = null;
        private static string _modelFileFullName = "";
        public string ProcessTokenizedSentence(StructuredSentence tokenizedSentence)
        {
            Debug.Assert(_root != null);

            string finalAnswer = "";
            string noAnswer = _root.GetAnswer();

            if (tokenizedSentence.SubSentences.Count > 0
                && tokenizedSentence.SubSentences[0].PhrasePart.KeyWordList.Count > 0)
            {
                // At least we have one meaningful phrase to process.
                foreach(SubSentence subSentence in tokenizedSentence.SubSentences)
                {
                    string s = ProcessListOfWords(subSentence.PhrasePart.KeyWordList);
                    if (s != noAnswer)
                    {
                        finalAnswer += s + " ";
                    }
                }
            }

            return finalAnswer.Length > 0 ? finalAnswer : noAnswer;
        }
        private string ProcessListOfWords(List<string> wordsList)
        {
            Debug.Assert(wordsList != null);
            Debug.Assert(_root != null);
            
            if (MoeDebugger.IsDebugging)
            {
                MoeDebugger.AddDebugInfo("===EnPrefixModel::ProcessListOfWords===");
            }
            MoeNode currentNode = _root;
            MoeNode answerNode = null;
            foreach (string word in wordsList)
            {
                currentNode = currentNode.MoveToNext(word);
                if (currentNode.IsFinalNode())
                {
                    answerNode = currentNode;
                }

                if (MoeDebugger.IsDebugging)
                {
                    MoeDebugger.AddDebugInfo(string.Format("<word> {0}; <is answer node> {1} ==> ",
                        word, currentNode.IsFinalNode().ToString()));
                }
            }
            if (MoeDebugger.IsDebugging)
            {
                MoeDebugger.AddDebugInfo("---------------------------------------");
            }

            return answerNode != null ? answerNode.GetAnswer() : _root.GetAnswer();
        }

        private MoeAnswer BuildTreePath(string questionPattern, params string[] answers)
        {
            questionPattern = questionPattern.ToLower();
            string[] wordList = questionPattern.Split(' ');
            MoeNode nextNode = null;
            MoeAnswer answer = new MoeAnswer(answers);
            foreach (string word in wordList)
            {
                if (nextNode == null)
                {
                    nextNode = _root.AddOutVertex(word, new MoeNode());
                }
                else
                {
                    nextNode = nextNode.AddOutVertex(word, new MoeNode());
                }
            }
            nextNode.SetItFinalNode();
            nextNode.SetAnswer(answer);

            return answer;
        }

        private MoeAnswer BuildTreePath(string questionPattern, MoeAnswer answer)
        {
            Debug.Assert(answer != null);

            questionPattern = questionPattern.ToLower();
            string[] wordList = questionPattern.Split(' ');
            MoeNode nextNode = null;
            foreach (string word in wordList)
            {
                if (nextNode == null)
                {
                    nextNode = _root.AddOutVertex(word, new MoeNode());
                }
                else
                {
                    nextNode = nextNode.AddOutVertex(word, new MoeNode());
                }
            }
            nextNode.SetItFinalNode();
            nextNode.SetAnswer(answer);

            return answer;
        }

        protected void InitializeModel()
        {
            _root = new MoeNode();
            Debug.Assert(_root != null);

            // What is your name? 1\My name is Moe.. 2\Call me Moe.. 3\I am named Moe.. 
            MoeAnswer answer = BuildTreePath("What is your name", "My name is Moe..", "Call me Moe..", "I am named as Moe..");

            // Who are you ? 1\My name is Moe.. 2\Call me Moe.. 3\I am named Moe..
            BuildTreePath("Who are you", answer);

            // What is the date today? 1\Some date I suppose. 2\Google it yourself, human!
            BuildTreePath("What is the date today", "MSome date I suppose.", "Google it yourself, human!");
        }

        protected void InitializeModelFromFile(string fileFullName)
        {
            Debug.Assert(fileFullName.Length > 0);

            _root = new MoeNode();
            Debug.Assert(_root != null);

            int nLines = 0;
            using (StreamReader reader = new StreamReader(fileFullName))
            {
                while (!reader.EndOfStream)
                {
                    string line = reader.ReadLine();
                    string[] part = line.Split('|');

                    // An question and an answer at least.
                    Debug.Assert(part.Count() > 1);
                    
                    string question = part[0];
                    string[] answers = new string[part.Count() - 1];
                    for (int i = 1; i < part.Count(); i++)
                    {
                        answers[i - 1] = part[i];
                    }
                    BuildTreePath(question, answers);
                    nLines++;
                }
            }
            if (MoeDebugger.IsDebugging)
            {
                MoeDebugger.AddDebugInfo("===EnPrefixTreeModel::InitializeModelFromFile===");
                MoeDebugger.AddDebugInfo(
                    string.Format("{0} question-answer pairs are loaded.", 
                    nLines));
                MoeDebugger.AddDebugInfo("------------------------------------------------");
            }
        }

        public static void SetModelFileFullName(string fileFullName)
        {
            _modelFileFullName = fileFullName;
        }

        private List<string> _allQuestionPatterns = null;
        public List<string> AllQuestions
        {
            get 
            {
                if (_allQuestionPatterns == null)
                {
                    _allQuestionPatterns = new List<string>();
                    foreach (var line in File.ReadAllLines(@".\Model\QA.txt"))
                    {
                        var fragments = line.Split('|');
                        _allQuestionPatterns.Add(fragments[0].Trim().ToLower());
                    }
                }
                Debug.Assert(_allQuestionPatterns != null);
                return _allQuestionPatterns;
            }
        }
    }
}
